<template>
  <div
      class="van-swipe-cell"
      @click="getClickHandler('cell')"
    >
    <div class="van-swipe-cell__wrapper" :style=wrapperStyle>
      <div
        ref="left"
        class="van-swipe-cell__left"
        @click="getClickHandler('left', true)"
      >
        <slot name="left"></slot>
      </div>
      <slot></slot>
      <div
        ref="right"
        class="van-swipe-cell__right"
        @click="getClickHandler('right', true)"
      >
        <slot name="right"></slot>
      </div>
    </div>
  </div>
</template>

<script>
const THRESHOLD = 0.15
const MIN_DISTANCE = 10
export default {
  name: 'avatar-item',
  props: {
    onClose: Function,
    beforeClose: Function,
    leftWidth: [Number, String],
    rightWidth: [Number, String],
    stopPropagation: Boolean,
    disabled: Boolean,
    name: {
      type: [Number, String],
      default: ''
    }
  },
  data () {
    return {
      offset: 0,
      dragging: false
    }
  },
  mounted () {
    this.bindTouchEvent(this.$el)
  },
  computed: {
    wrapperStyle () {
      return {
        transform: `translate3d(${this.offset}px, 0, 0)`,
        transitionDuration: this.dragging ? '0s' : '.6s'
      }
    },
    computedLeftWidth () {
      return +this.leftWidth || this.getWidthByRef('left')
    },
    computedRightWidth () {
      return +this.rightWidth || this.getWidthByRef('right')
    }
  },
  methods: {
    getWidthByRef (ref) {
      if (this.$refs[ref]) {
        const rect = this.$refs[ref].getBoundingClientRect()
        return rect.width
      }
      return 0
    },
    getClickHandler (position, stop) {
      return (event) => {
        if (stop) {
          event.stopPropagation()
        }
        this.onClick(position)
      }
    },
    onClick (position = 'outside') {
      this.$emit('click', position)

      if (this.opened && !this.lockClick) {
        if (this.beforeClose) {
          this.beforeClose({
            position,
            name: this.name,
            instance: this
          })
        } else if (this.onClose) {
          this.onClose(position, this, { name: this.name })
        } else {
          this.close(position)
        }
      }
    },
    // @exposed-api
    open (position) {
      const offset =
        position === 'left' ? this.computedLeftWidth : -this.computedRightWidth

      this.opened = true
      this.offset = offset

      this.$emit('open', {
        position,
        name: this.name,
        // @deprecated
        // should be removed in next major version
        detail: this.name
      })
    },

    // @exposed-api
    close (position) {
      this.offset = 0

      if (this.opened) {
        this.opened = false
        this.$emit('close', {
          position,
          name: this.name
        })
      }
    },
    onTouchStart (event) {
      if (this.disabled) {
        return
      }

      this.startOffset = this.offset
      this.touchStart(event)
    },
    onTouchMove (event) {
      if (this.disabled) {
        return
      }

      this.touchMove(event)

      if (this.direction === 'horizontal') {
        this.dragging = true
        this.lockClick = true

        const isPrevent = !this.opened || this.deltaX * this.startOffset < 0

        if (isPrevent) {
          this.preventDefault(event, this.stopPropagation)
        }

        this.offset = this.range(
          this.deltaX + this.startOffset,
          -this.computedRightWidth,
          this.computedLeftWidth
        )
      }
    },
    onTouchEnd () {
      if (this.disabled) {
        return
      }

      if (this.dragging) {
        this.toggle(this.offset > 0 ? 'left' : 'right')
        this.dragging = false

        // compatible with desktop scenario
        setTimeout(() => {
          this.lockClick = false
        }, 0)
      }
    },
    toggle (direction) {
      const offset = Math.abs(this.offset)
      const threshold = this.opened ? 1 - THRESHOLD : THRESHOLD
      const { computedLeftWidth, computedRightWidth } = this

      if (
        computedRightWidth &&
        direction === 'right' &&
        offset > computedRightWidth * threshold
      ) {
        this.open('right')
      } else if (
        computedLeftWidth &&
        direction === 'left' &&
        offset > computedLeftWidth * threshold
      ) {
        this.open('left')
      } else {
        this.close()
      }
    },
    touchStart (event) {
      this.resetTouchStatus()
      this.startX = event.touches[0].clientX
      this.startY = event.touches[0].clientY
    },

    touchMove (event) {
      const touch = event.touches[0]
      // Fix: Safari back will set clientX to negative number
      this.deltaX = touch.clientX < 0 ? 0 : touch.clientX - this.startX
      this.deltaY = touch.clientY - this.startY
      this.offsetX = Math.abs(this.deltaX)
      this.offsetY = Math.abs(this.deltaY)
      this.direction =
        this.direction || this.getDirection(this.offsetX, this.offsetY)
    },
    getDirection (x, y) {
      if (x > y && x > MIN_DISTANCE) {
        return 'horizontal'
      }

      if (y > x && y > MIN_DISTANCE) {
        return 'vertical'
      }

      return ''
    },
    resetTouchStatus () {
      this.direction = ''
      this.deltaX = 0
      this.deltaY = 0
      this.offsetX = 0
      this.offsetY = 0
    },
    bindTouchEvent (el) {
      const { onTouchStart, onTouchMove, onTouchEnd } = this

      this.on(el, 'touchstart', onTouchStart)
      this.on(el, 'touchmove', onTouchMove)

      if (onTouchEnd) {
        this.on(el, 'touchend', onTouchEnd)
        this.on(el, 'touchcancel', onTouchEnd)
      }
    },
    on (target, event, handler) {
      target.addEventListener(
        event,
        handler,
        false
      )
    },
    range (num, min, max) {
      return Math.min(Math.max(num, min), max)
    },
    preventDefault (event, isStopPropagation) {
      if (typeof event.cancelable !== 'boolean' || event.cancelable) {
        event.preventDefault()
      }
      if (isStopPropagation) {
        event.stopPropagation()
      }
    }
  }
}
</script>

<style scoped>
.van-swipe-cell {
  position: relative;
  overflow: hidden;
  cursor: grab;
  background-color: #fff;
  margin-top: 10px;
}
.van-swipe-cell__wrapper {
   transition-timing-function: cubic-bezier(0.18, 0.89, 0.32, 1);
   transition-property: transform;
 }
.van-swipe-cell__left,
.van-swipe-cell__right {
   position: absolute;
   top: 0;
   height: 100%;
 }
.van-swipe-cell__left {
  left: 0;
  transform: translate3d(-100%, 0, 0);
}
.van-swipe-cell__right {
   right: 0;
   transform: translate3d(100%, 0, 0);
 }
</style>
